//    Copyright (C) Kherty.  All rights reserved.
#region

using System.Collections.Generic;
using System.Diagnostics.Contracts;
using OpenLS.Core.Serialization;
using System.Diagnostics;

#endregion

namespace OpenLS.Spreadsheet
{
    public class ColorScaleFormattingRule : ConditionalFormattingRule
    {
        internal ColorScaleFormattingRule()
        {
        }
        public ColorScaleFormattingRule(IEnumerable<SpreadsheetColor> colors)
        {
            foreach (var color in colors)
            {
                ColorScale.Colors.Add(color);
            }
            ColorScale.Conditions.Add(new ConditionalFormatValueObject {Type = ConditionalFormatValueObjectType.Minimum});
            int length = this.ColorScale.Colors.Count;
            for (int i = 0; i < length - 2; i++)
                ColorScale.Conditions.Add(new ConditionalFormatValueObject
                                              {
                                                  Type = ConditionalFormatValueObjectType.Percent,
                                                  Value = (100*(i + 1)/(length - 1)).ToString()
                                              });
            ColorScale.Conditions.Add(new ConditionalFormatValueObject {Type = ConditionalFormatValueObjectType.Maximum});
            Contract.Assert(ColorScale.Conditions.Count == ColorScale.Colors.Count);
        }
        
        
        public ColorScaleFormattingRule(params SpreadsheetColor[] colors): this ((IEnumerable<SpreadsheetColor>)colors)
        {
        }

        internal override void ReadInner(ReadContext c)
        {
            _colorScale = c.ReadOptionalElement("colorScale", () => new ColorScale());
        }

        internal override void WriteInner(WriteContext c)
        {
            c.WriteOptionalElement(ColorScale);
        }

        internal override ConditionalFormattingRule Clone()
        {
            var result = (ColorScaleFormattingRule) MemberwiseClone();
            result._colorScale = ColorScale.Clone();
            return result;
        }

        internal override ConditionalFormattingType Type
        {
            get { return ConditionalFormattingType.ColorScale; }
        }

        private ColorScale _colorScale = new ColorScale();

        internal ColorScale ColorScale
        {
            get { return _colorScale; }
        }
        public IEnumerable<SpreadsheetColor> Colors
        {
            get { return ColorScale.Colors; }
        }

        internal override bool Apply(Cell cell)
        {
            if (!(cell.Value is double))
            {
                cell.ColorScale = null;
                return true;
            }
            var n = (double) cell.Value;
            for (int i = 0; i < colorValues.Count - 1; i++)
            {
                double firstValue = colorValues[i];
                double secondValue = colorValues[i + 1];
                if (isBetween(n, firstValue, secondValue))
                {
                    double ratio = 0;
                    if (firstValue != secondValue)
                    {
                        ratio = (n - firstValue)/(secondValue - firstValue);
                    }
                    cell.ColorScale = new ColorScaleInformation(ColorScale.Colors[i], ColorScale.Colors[i + 1],
                                                                ratio);
                    break;
                }
            }
            return true;
        }

        private static bool isBetween(double value, double v1, double v2)
        {
            if (value >= v1 && v2 >= value)
                return true;
            if (value <= v1 && v2 <= value)
                return true;
            return false;
        }

        internal override void ApplyRange(Range range)
        {
            Distribution = new CellValueDistribution(range);
            colorValues = new List<double>();
            if (Distribution.IsEmpty)
                return;
            foreach (var o in ColorScale.Conditions)
            {
                colorValues.Add(Distribution.GetValue(o));
            }
        }

        private CellValueDistribution Distribution;
        private List<double> colorValues = new List<double>();
    }
}